package se.sics.kompics.tutorial.tutorial2;

import java.util.Set;

import org.apache.log4j.PropertyConfigurator;

import se.kth.ict.id2203.flp2p.FairLossPointToPointLink;
import se.kth.ict.id2203.flp2p.delay.DelayDropLink;
import se.kth.ict.id2203.flp2p.delay.DelayDropLinkInit;
import se.kth.ict.id2203.pp2p.delay.DelayLink;
import se.kth.ict.id2203.pp2p.delay.DelayLinkInit;
import se.sics.kompics.Component;
import se.sics.kompics.ComponentDefinition;
import se.sics.kompics.Fault;
import se.sics.kompics.Handler;
import se.sics.kompics.Kompics;
import se.sics.kompics.address.Address;
import se.sics.kompics.launch.Topology;
import se.sics.kompics.network.Network;
import se.sics.kompics.network.mina.MinaNetwork;
import se.sics.kompics.network.mina.MinaNetworkInit;
import se.sics.kompics.timer.Timer;
import se.sics.kompics.timer.java.JavaTimer;
import se.sics.kompics.tutorial.tutorial2.lazyProbabilisticBcast.LazyPBImpl;
import se.sics.kompics.tutorial.tutorial2.lazyProbabilisticBcast.PbreliableBroadcastInit;
import se.sics.kompics.tutorial.tutorial2.unreliableBroadcast.UnreliableBroadcast;
import se.sics.kompics.tutorial.tutorial2.unreliableBroadcast.UnreliableBroadcastImpl;
import se.sics.kompics.tutorial.tutorial2.unreliableBroadcast.UnreliableBroadcastInit;

public class Main extends ComponentDefinition {
	static {
		PropertyConfigurator.configureAndWatch("log4j.properties");
	}
	private static int selfId;
	private static String commandScript;
	Topology topology = Topology.load(System.getProperty("topology"), selfId);

	/**
	 * The main method.
	 * 
	 * @param args
	 *            the arguments
	 */
	public static void main(String[] args) {
		selfId = Integer.parseInt(args[0]);
		commandScript = args[1];

		Kompics.createAndStart(Main.class);
	}
	
	/**
	 * Instantiates a new assignment1
	 */
	public Main() {
		// create components
		Component time = create(JavaTimer.class);
		Component network = create(MinaNetwork.class);
		Component pp2p = create(DelayLink.class);
		Component flp2p = create(DelayDropLink.class);
		Component app = create(LazyPBImpl.class);
		Component unBr = create(UnreliableBroadcastImpl.class);

		// handle possible faults in the components
		subscribe(handleFault, time.control());
		subscribe(handleFault, network.control());
		subscribe(handleFault, pp2p.control());
		subscribe(handleFault, flp2p.control());
		subscribe(handleFault, app.control());
		subscribe(handleFault, unBr.control());

		// initialize the components
		Address self = topology.getSelfAddress();
		Set<Address> neighborSet = topology.getNeighbors(self);

		trigger(new MinaNetworkInit(self, 5), network.control());
		trigger(new DelayLinkInit(topology), pp2p.control());
		trigger(new DelayDropLinkInit(topology, 0), flp2p.control());
		trigger(new PbreliableBroadcastInit(commandScript, neighborSet, self), app.control());
		trigger(new UnreliableBroadcastInit(neighborSet, self), unBr.control());

		// connect the components
		connect(app.required(FairLossPointToPointLink.class), flp2p
				.provided(FairLossPointToPointLink.class));
		connect(app.required(Timer.class), time.provided(Timer.class));
		connect(app.required(UnreliableBroadcast.class), unBr.provided(UnreliableBroadcast.class));
		
		connect(unBr.required(FairLossPointToPointLink.class), flp2p
				.provided(FairLossPointToPointLink.class));

		connect(pp2p.required(Timer.class), time.provided(Timer.class));
		connect(pp2p.required(Network.class), network.provided(Network.class));

		connect(flp2p.required(Timer.class), time.provided(Timer.class));
		connect(flp2p.required(Network.class), network.provided(Network.class));
	}
	
	Handler<Fault> handleFault = new Handler<Fault>() {
		public void handle(Fault fault) {
			fault.getFault().printStackTrace(System.err);
		}
	};

}
